package priv.utrix.micro.controller;

import cn.hutool.core.util.StrUtil;
import com.google.common.base.Preconditions;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.*;
import priv.utrix.micro.constant.AuthConstants;
import priv.utrix.micro.json.R;
import priv.utrix.micro.utils.CryptoUtil;
import priv.utrix.micro.utils.RedisUtil;
import priv.utrix.micro.vo.LoginSuccessVo;

import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
import java.util.Map;
import java.util.Optional;

import static cn.hutool.core.map.MapUtil.getStr;
import static cn.hutool.core.util.StrUtil.isNotBlank;
import static priv.utrix.micro.constant.UserMapKeyConstants.USERNAME;
import static priv.utrix.micro.json.R.failed;
import static priv.utrix.micro.json.R.ok;

@Slf4j
@RestController
@RequestMapping("/oauth")
@RequiredArgsConstructor
public class AuthController {

   final RedisUtil redisUtil;
   final TokenEndpoint tokenEndpoint;
   final RedisTemplate<String,Object> redisTemplate;

    /**
     * 自定义获取token接口, 刷新token
     *
     * @param principal  获取人信息
     * @param parameters 获取token信息集合
     * @return priv.utrix.micro.json.R<java.lang.String>
     * @throws HttpRequestMethodNotSupportedException 当请求方法不支持时抛出
     */
    @PostMapping("/token")
    public R<LoginSuccessVo> postAccessToken(Principal principal, @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
        log.info("Make token's thread: {}", Thread.currentThread().getId());
        String clientId = getStr(parameters, AuthConstants.CLIENT_ID);
        Preconditions.checkState(isNotBlank(clientId), "Client id is empty");

        R<LoginSuccessVo> isRepeatLogin = verifyUserRepeatLogin(parameters);
        if (isRepeatLogin != null) return isRepeatLogin;

        OAuth2AccessToken oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();
        Preconditions.checkNotNull(oAuth2AccessToken);

        String username = getStr(oAuth2AccessToken.getAdditionalInformation(), USERNAME);
        String encryptMd5 = CryptoUtil.encryptMd5(username);
        redisUtil.strSecondsSet(encryptMd5, oAuth2AccessToken.getValue(), oAuth2AccessToken.getExpiresIn());

        LoginSuccessVo loginSuccessVo = new LoginSuccessVo().setJwt(encryptMd5)
                .setExpireTime(oAuth2AccessToken.getExpiresIn())
                .setRefreshToken(oAuth2AccessToken.getRefreshToken().getValue());
        return ok(loginSuccessVo);
    }

    /**
     * 自定义登出接口
     *
     * @param request http请求体
     * @return priv.utrix.micro.json.R<java.lang.String>
     */
    @DeleteMapping("/logout")
    public R<Object> logout(HttpServletRequest request) {
        String prefixAndJti = request.getHeader(AuthConstants.JWT_TOKEN_HEADER);
        return Optional.ofNullable(prefixAndJti)
                .map(key -> key.replace(AuthConstants.JWT_TOKEN_PREFIX, Strings.EMPTY))
                .filter(StrUtil::isNotBlank)
                .filter(jti -> jti.length() == 32)
                .map(jti -> {
                    redisUtil.remove(jti);
                    return ok();
                }).orElseGet(R::failed);
    }

    /**
     * 监控用户是否重复登录
     *
     * @param parameters 登录参数
     * @return priv.utrix.micro.json.R<priv.utrix.micro.feign.vo.LoginSuccessVo>
     */
    private R<LoginSuccessVo> verifyUserRepeatLogin(@RequestParam Map<String, String> parameters) {
        String username = getStr(parameters, USERNAME);
        if (StrUtil.isNotEmpty(username)) {
            // 监测用户重复登陆操作
            String redisCacheKey = CryptoUtil.encryptMd5(username);
            if (redisUtil.isKey(redisCacheKey)) {
                return failed("Repeat login, username %s", username);
            }
        }

        return null;
    }

}
